/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.mapagay.entidades.mapa;

import br.com.mapagay.conexao.Conexao;
import br.com.mapagay.entidades.Point;
import br.com.mapagay.entidades.usuario.Entidade;
import br.com.mapagay.exceptions.EntidadeNaoEncontrada;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Lauro
 */
public class Lugar extends Entidade implements Point {

    private String nome;
    private String imagem;
    private double latitude;
    private double longitude;
    private Categoria categoria;
    private Outdoor outdoor;
    private boolean retirarOutdoor = false;

    public Lugar() {
    }

    public Lugar(Integer id) {
        super(id);
    }

    private Lugar(Integer id, String nome, String imagem, double latitude, double longitude, Categoria categoria, Outdoor outdoor) {
        super(id);
        this.nome = nome;
        this.imagem = imagem;
        this.latitude = latitude;
        this.longitude = longitude;
        this.categoria = categoria;
        this.outdoor = outdoor;
    }

    //<editor-fold defaultstate="expanded" desc="Metodos CRUD.">
    protected boolean buscar() throws EntidadeNaoEncontrada {
        return buscar(null);
    }

    @Override
    protected boolean buscar(Connection conexao) throws EntidadeNaoEncontrada {
        boolean fecharConexao = conexao == null ? true : false;
        try {
            conexao = conexao == null ? Conexao.getConexao() : conexao;
            String sql = getSQLBuscarPorId();
            PreparedStatement query = conexao.prepareStatement(sql);
            query.setInt(1, this.getId());
            ResultSet result = query.executeQuery();
            if (result.next()) {
                this.setNome(result.getString("l.nome"));
                this.setImagem(result.getString("l.imagem"));
                this.setLatitude(result.getDouble("l.latitude"));
                this.setLongitude(result.getDouble("l.longitude"));
                this.setCategoria(new Categoria(result.getInt("c.id"), result.getString("c.nome")));
                if (result.getObject("o.id") != null) {
                    this.setOutdoor(new Outdoor(result.getInt("o.id"), result.getString("o.imagem")));
                }
            } else {
                throw new EntidadeNaoEncontrada();
            }
            if (fecharConexao) {
                Conexao.close(conexao, query, result);
            }
            return true;
        } catch (SQLException ex) {
            if (fecharConexao) {
                Conexao.close(conexao);
            }
            return false;
        }
    }

    public static List<Lugar> buscarTodos() {
        return buscarTodos(null);
    }

    public static List<Lugar> buscarTodos(Categoria categoria) {
        Connection conexao = Conexao.getConexao();
        List<Lugar> lugares = new ArrayList<Lugar>();
        try {
            String sql = categoria == null ? getSQLBuscarTodos() : getSQLBuscarPorCategoria();
            PreparedStatement query = conexao.prepareStatement(sql);
            if (categoria != null) {
                query.setInt(1, categoria.getId());
            }
            ResultSet result = query.executeQuery();
            while (result.next()) {
                Lugar lugar = new Lugar(result.getInt("l.id"),
                        result.getString("l.nome"),
                        result.getString("l.imagem"),
                        result.getDouble("l.latitude"),
                        result.getDouble("l.longitude"),
                        new Categoria(result.getInt("c.id"), result.getString("c.nome")),
                        ((result.getObject("o.id") != null) ? new Outdoor(result.getInt("o.id"), result.getString("o.imagem")) : null));
                lugares.add(lugar);
            }
            Conexao.close(conexao, query, result);
        } catch (SQLException ex) {
            Conexao.close(conexao);
        } finally {
            return lugares;
        }
    }

    public static List<Lugar> buscarLugaresMaisProximos(double latitude, double longitude, double raio, int max) {
        return buscarLugaresMaisProximos(latitude, longitude, null, raio, max);
    }

    public static List<Lugar> buscarLugaresMaisProximos(double latitude, double longitude, int[] categorias, double raio, int max) {
        Connection conexao = Conexao.getConexao();
        List<Lugar> lugares = new ArrayList<Lugar>();
        try {
            String sql = (categorias != null && categorias.length > 0) ? getSQLBuscarProximosPorCategoria(categorias) : getSQLBuscarProximos();
            PreparedStatement query = conexao.prepareStatement(sql);
            query.setDouble(1, latitude);
            query.setDouble(2, longitude);
            query.setDouble(3, latitude);
            query.setDouble(4, raio);
            if (categorias != null && categorias.length > 0) {
                for (int i = 1; i <= categorias.length; i++) {
                    query.setInt((4 + i), categorias[i]);
                }
            }
            ResultSet result = query.executeQuery();
            while (result.next()) {
                Lugar lugar = new Lugar(result.getInt("l.id"),
                        result.getString("l.nome"),
                        result.getString("l.imagem"),
                        result.getDouble("l.latitude"),
                        result.getDouble("l.longitude"),
                        new Categoria(result.getInt("c.id"), result.getString("c.nome")),
                        ((result.getObject("o.id") != null) ? new Outdoor(result.getInt("o.id"), result.getString("o.imagem")) : null));
                lugares.add(lugar);
            }
            Conexao.close(conexao, query, result);
        } catch (SQLException ex) {
            Conexao.close(conexao);
        } finally {
            return lugares;
        }
    }

    @Override
    protected boolean inserir(Connection conexao) {
        boolean fecharConexao = conexao == null ? true : false;
        try {
            conexao = conexao == null ? Conexao.getConexao() : conexao;
            String sql = getSQLInsercao();
            PreparedStatement query = conexao.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            query.setString(1, this.getNome());
            query.setString(2, this.getImagem());
            query.setDouble(3, this.getLatitude());
            query.setDouble(4, this.getLongitude());
            query.setInt(5, this.getCategoria().getId());
            query.executeUpdate();
            ResultSet result = query.getGeneratedKeys();
            result.first();
            int result_id = result.getInt(1);
            this.setId(result_id);
            if (this.outdoor != null) {
                if (!this.outdoor.inserir(conexao)) {
                    throw new SQLException();
                }
            }
            if (fecharConexao) {
                Conexao.close(conexao, query, result);
            }
            return true;
        } catch (SQLException ex) {
            if (fecharConexao) {
                Conexao.close(conexao);
            }
            return false;
        }
    }

    @Override
    protected boolean atualizar(Connection conexao) {
        boolean fecharConexao = conexao == null ? true : false;
        try {
            conexao = conexao == null ? Conexao.getConexao() : conexao;
            String sql = getSQLAtualizacao();
            PreparedStatement query = conexao.prepareStatement(sql);
            query.setString(1, this.getNome());
            query.setString(2, this.getImagem());
            query.setDouble(3, this.getLatitude());
            query.setDouble(4, this.getLongitude());
            query.setInt(5, this.getCategoria().getId());
            query.setInt(6, this.getId());
            query.executeUpdate();
            if (retirarOutdoor) {
                this.outdoor.excluir(conexao);
            }
            if (fecharConexao) {
                Conexao.close(conexao, query);
            }
            return true;
        } catch (SQLException ex) {
            if (fecharConexao) {
                Conexao.close(conexao);
            }
            return false;
        }
    }

    @Override
    protected boolean excluir(Connection conexao) {
        boolean fecharConexao = conexao == null ? true : false;
        try {
            conexao = conexao == null ? Conexao.getConexao() : conexao;
            String sql = getSQLExclusao();
            PreparedStatement query = conexao.prepareStatement(sql);
            query.setInt(1, this.getId());
            query.executeUpdate();
            if (fecharConexao) {
                Conexao.close(conexao, query);
            }
            return true;
        } catch (SQLException ex) {
            if (fecharConexao) {
                Conexao.close(conexao);
            }
            return false;
        }
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Metodos Getters & Setters.">
    @Override
    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getImagem() {
        return imagem;
    }

    public void setImagem(String imagem) {
        this.imagem = imagem;
    }

    @Override
    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    @Override
    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }

    public Categoria getCategoria() {
        return categoria;
    }

    public void setCategoria(Categoria categoria) {
        this.categoria = categoria;
    }

    public Outdoor getOutdoor() {
        return outdoor;
    }

    public void setOutdoor(Outdoor outdoor) {
        if (this.outdoor != null && outdoor == null) {
            retirarOutdoor = true;
        }
        this.outdoor = outdoor;
    }

    @Override
    public String getThumb() {
        return imagem;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Strings SQL.">
    private static String getSQLBuscarPorId() {
        StringBuilder sql = new StringBuilder();
        sql.append(" SELECT l.*, c.*, o.* ");
        sql.append(" FROM lugar l ");
        sql.append(" INNER JOIN categoria c ON c.id = l.categoria");
        sql.append(" LEFT JOIN outdoor o ON o.id = l.id");
        sql.append(" WHERE ");
        sql.append(" id = ? ");
        return sql.toString();
    }

    private static String getSQLBuscarTodos() {
        StringBuilder sql = new StringBuilder();
        sql.append(" SELECT l.*, c.*, o.* ");
        sql.append(" FROM lugar l ");
        sql.append(" INNER JOIN categoria c ON c.id = l.categoria");
        sql.append(" LEFT JOIN outdoor o ON o.id = l.id");
        return sql.toString();
    }

    private static String getSQLBuscarPorCategoria() {
        StringBuilder sql = new StringBuilder();
        sql.append(" SELECT l.*, c.*, o.* ");
        sql.append(" FROM lugar l ");
        sql.append(" INNER JOIN categoria c ON c.id = l.categoria");
        sql.append(" LEFT JOIN outdoor o ON o.id = l.id");
        sql.append(" WHERE ");
        sql.append(" c.id = ? ");
        return sql.toString();
    }

    private static String getSQLBuscarProximos() {
        return getSQLBuscarProximosPorCategoria(null);
    }

    private static String getSQLBuscarProximosPorCategoria(int[] categorias) {
        StringBuilder sql = new StringBuilder();
        sql.append(" SELECT l.*, c.*, o.*, ");
        sql.append("    ( 3959 * acos(  ");
        sql.append("        cos( radians(?) )      ");
        sql.append("        * cos( radians( l.latitude ) )  ");
        sql.append("        * cos( radians(l.longitude) - radians(?))  ");
        sql.append("        + sin(radians(?)) * sin( radians(l.latitude))  ");
        sql.append("        )");
        sql.append("    ) AS distancia ");
        sql.append(" FROM lugar l ");
        sql.append(" INNER JOIN categoria c ON c.id = l.categoria");
        sql.append(" LEFT JOIN outdoor o ON o.id = l.id");
        if (categorias != null && categorias.length > 0) {
            sql.append("    WHERE c.id IN (   ");
            for (int i = 1; i <= categorias.length; i++) {
                sql.append("    ?   ");
                if (i < categorias.length) {
                    sql.append("    ,   ");
                }
            }
            sql.append("    )   ");
        }
        sql.append("    HAVING distancia < ?    ");
        sql.append("    ORDER BY distancia  ");
        return sql.toString();
    }

    private static String getSQLInsercao() {
        StringBuilder sql = new StringBuilder();
        sql.append(" INSERT INTO lugar ( ");
        sql.append(" nome, ");
        sql.append(" imagem, ");
        sql.append(" latitude, ");
        sql.append(" longitude, ");
        sql.append(" categoria ");
        sql.append(" ) VALUES ( ");
        sql.append(" ?,  ");
        sql.append(" ?,  ");
        sql.append(" ?,  ");
        sql.append(" ?,  ");
        sql.append(" ?  ");
        sql.append(" )  ");
        return sql.toString();
    }

    private static String getSQLAtualizacao() {
        StringBuilder sql = new StringBuilder();
        sql.append(" UPDATE lugar SET ");
        sql.append(" nome = ?, ");
        sql.append(" imagem = ?, ");
        sql.append(" latitude = ?, ");
        sql.append(" longitude = ?, ");
        sql.append(" categoria = ? ");
        sql.append(" WHERE ");
        sql.append(" id = ? ");
        return sql.toString();
    }

    private static String getSQLExclusao() {
        StringBuilder sql = new StringBuilder();
        sql.append(" DELETE FROM lugar ");
        sql.append(" WHERE ");
        sql.append(" id = ? ");
        return sql.toString();
    }
    //</editor-fold>    
}
